import { computed, defineComponent, ref, onMounted, onUnmounted, SetupContext, watch } from 'vue';
import { ResponseToolbarProps, responseToolbarProps } from './response-toolbar.props';
import { ResponseToolbarDropDownItem } from './types/response-toolbar-dropdwon-item';
import { ResponseToolbarItem } from './types/response-toolbar-item';
import getDropdown from './components/dropdown/toolbar-dropdown.component';
import { useIcon } from './composition/use-icon';
import { useToolbarItem } from './composition/use-toolbar-item';

const { buildResponseToolbarItems } = useToolbarItem();
export default defineComponent({
    name: 'FResponseToolbar',
    props: responseToolbarProps,
    emits: ['Click'],
    setup(props: ResponseToolbarProps) {
        const toolbarItems = ref<(ResponseToolbarItem | ResponseToolbarDropDownItem)[]>(buildResponseToolbarItems(props.items));
        const resizedContainer = ref<any>();
        const resizedContent = ref<any>();
        const alignment = ref(props.alignment);
        const defaultDropdownOptions = { id: '__more_buttons__', text: 'More' };
        const defaultDropdown = ref(new ResponseToolbarDropDownItem(defaultDropdownOptions));
        const useIconComposition = useIcon();

        const shouldShowDefaultDropdown = computed(() => defaultDropdown.value.children.length > 0);

        const responseToolbarClass = computed(() => {
            const classObject = {
                'f-toolbar': true,
                'f-response-toolbar': true,
                'position-relative': true
            } as Record<string, boolean>;
            return classObject;
        });

        const resizedContainerClass = computed(() => {
            const classObject = {
                'd-flex': true,
                'flex-nowrap': true,
                'justify-content-end': alignment.value === 'right',
                'justify-content-start': alignment.value === 'left'
            } as Record<string, boolean>;
            return classObject;
        });

        const { renderToolbarDropdown, clearAllDropDown } = getDropdown(props, useIconComposition);

        function collapseAllDropdownMenu() {
            toolbarItems.value
                .filter((item) => item.children && item.children.length > 0)
                .forEach((item) => {
                    item.expanded = false;
                });
            defaultDropdown.value.expanded = false;
            clearAllDropDown();
        }

        function iconClass(item: ResponseToolbarItem | ResponseToolbarDropDownItem) {
            const classObject = {
                'f-icon': true
            } as Record<string, boolean>;
            if (item.icon) {
                const classNames = item.icon.trim().split(' ');
                if (classNames && classNames.length) {
                    classNames.reduce((result: Record<string, boolean>, className: string) => {
                        result[className] = true;
                        return result;
                    }, classObject);
                }
            }
            return classObject;
        }

        function shouldShowIcon(item: ResponseToolbarItem | ResponseToolbarDropDownItem) {
            return !!(item.icon && item.icon.trim());
        }

        function buttonClass(item: ResponseToolbarItem | ResponseToolbarDropDownItem) {
            const classObject = {
                btn: true,
                'f-rt-btn': true,
                'f-btn-ml': alignment.value === 'right',
                'f-btn-mr': alignment.value === 'left',
                'btn-icontext': !!(item.icon && item.icon.trim())
            } as Record<string, boolean>;
            if (item.class) {
                const classNames = item.class.split(' ');
                if (classNames && classNames.length) {
                    classNames.reduce((result: Record<string, boolean>, className: string) => {
                        result[className] = true;
                        return result;
                    }, classObject);
                }
            }
            return classObject;
        }

        function renderToolbarButton(item: ResponseToolbarItem) {
            return (
                <button
                    type="button"
                    class={buttonClass(item)}
                    id={item.id}
                    disabled={!item.enable}
                    onClick={(payload: MouseEvent) => item.onClick(payload, item.id)}>
                    {shouldShowIcon(item) && <i class={iconClass(item)}></i>}
                    {item.text}
                </button>
            );
        }
        const itemsToHide = new Map<string, boolean>();
        const hiddenItems: { id: string; width: number }[] = [];

        function toResizeToolbarItems(containerWidth: number) {
            const contentElement = resizedContent.value as HTMLElement;
            let availableSpace = containerWidth;
            const allElements = Array.from(contentElement.children);
            const moreElement = allElements[allElements.length - 1].id === '__more_buttons__' ? allElements[allElements.length - 1] : null;
            if (moreElement) {
                const marginLeftOfMoreElement = (moreElement as HTMLElement).computedStyleMap().get('margin-left') as CSSUnitValue;
                const marginRightOfMoreElement = (moreElement as HTMLElement).computedStyleMap().get('margin-right') as CSSUnitValue;
                const moreElementWidth =
                    (marginLeftOfMoreElement ? marginLeftOfMoreElement.value : 0) +
                    (moreElement as HTMLElement).getBoundingClientRect().width +
                    (marginRightOfMoreElement ? marginRightOfMoreElement.value : 0);
                availableSpace -= moreElementWidth;
            }
            const toolbarItemElements = allElements.filter((element: Element) => element.id !== '__more_buttons__');
            // eslint-disable-next-line no-restricted-syntax
            for (const toolbarItem of toolbarItemElements) {
                const marginLeft = (toolbarItem as HTMLElement).computedStyleMap().get('margin-left') as CSSUnitValue;
                const marginRight = (toolbarItem as HTMLElement).computedStyleMap().get('margin-right') as CSSUnitValue;
                const itemWidth =
                    (marginLeft ? marginLeft.value : 0) +
                    (toolbarItem as HTMLElement).getBoundingClientRect().width +
                    (marginRight ? marginRight.value : 0);

                if (availableSpace < itemWidth) {
                    itemsToHide.set((toolbarItem as HTMLElement).id, true);
                    hiddenItems.push({ id: toolbarItem.id, width: itemWidth });
                } else {
                    availableSpace -= itemWidth;
                }
            }
            if (hiddenItems.length) {
                for (let index = hiddenItems.length - 1; index >= 0; index--) {
                    const latestHiddenItemWidth = hiddenItems[index].width;
                    if (availableSpace >= latestHiddenItemWidth) {
                        availableSpace -= latestHiddenItemWidth;
                        itemsToHide.delete(hiddenItems[index].id);
                        hiddenItems.pop();
                    } else {
                        break;
                    }
                }
            }
            const currentDropdownOptions = Object.assign({}, defaultDropdown.value);
            currentDropdownOptions.children = [];
            const dropdownItem = new ResponseToolbarDropDownItem(currentDropdownOptions);
            const availableItems = toolbarItems.value.reduce(
                (
                    items: (ResponseToolbarItem | ResponseToolbarDropDownItem)[],
                    toolbarItem: ResponseToolbarItem | ResponseToolbarDropDownItem
                ) => {
                    if (itemsToHide.has(toolbarItem.id)) {
                        dropdownItem.children.push(toolbarItem);
                    }
                    toolbarItem.visible = !itemsToHide.has(toolbarItem.id);
                    items.push(toolbarItem);
                    return items;
                },
                []
            );
            defaultDropdown.value = dropdownItem;
            toolbarItems.value = availableItems;
        }

        const observer = new ResizeObserver((entries: ResizeObserverEntry[]) => {
            if (entries.length) {
                const responseContainerEntry = entries[0];
                const containerWidth = responseContainerEntry.contentRect.width;
                const contentElement = resizedContent.value;
                const contentWidth = contentElement.getBoundingClientRect().width;
                if (containerWidth < contentWidth || hiddenItems.length) {
                    toResizeToolbarItems(containerWidth);
                }
            }
        });

        onMounted(() => {
            const element = resizedContainer.value;
            observer.observe(element);
            document.body.addEventListener('click', collapseAllDropdownMenu);
        });

        onUnmounted(() => {
            observer.disconnect();
            document.body.removeEventListener('click', collapseAllDropdownMenu);
        });

        watch(
            () => props.items,
            () => {
                toolbarItems.value = buildResponseToolbarItems(props.items);
            }
        );

        return () => {
            return (
                <div class={responseToolbarClass.value}>
                    <div ref={resizedContainer} class={resizedContainerClass.value}>
                        <div ref={resizedContent} class="d-inline-block f-response-content" style="white-space: nowrap;">
                            {toolbarItems.value
                                .filter((item) => item.visible)
                                .map((item) => {
                                    return item.children && item.children.length > 0
                                        ? renderToolbarDropdown(item as ResponseToolbarDropDownItem)
                                        : renderToolbarButton(item as ResponseToolbarItem);
                                })}
                            {shouldShowDefaultDropdown.value && renderToolbarDropdown(defaultDropdown.value)}
                        </div>
                    </div>
                </div>
            );
        };
    }
});
